using UnityEngine.Rendering;
using System.Collections.Generic;
using UnityEngine;

public class HiZBuffer : MonoBehaviour
{
    #region Variables

    public Shader HizGenerateShader;
    public Camera mainCamera = null;

    private RenderTexture m_hizDepthBuffer;
    private Material m_generateBufferMat;
    private CommandBuffer m_cb;
    private Vector2 m_textureSize;
    private int m_HiZCount = 0;

    #endregion

    public RenderTexture Texture { get { return m_hizDepthBuffer; } }
    public Vector2 textureSize { get { return m_textureSize; } }
    void Awake()
    {
        m_generateBufferMat = new Material(HizGenerateShader);
        mainCamera.depthTextureMode = DepthTextureMode.Depth;
    }

    // Update is called once per frame
    private void OnPreRender()
    {
        if (m_hizDepthBuffer == null)
        {
            InitializeHiZBuffer();
        }
        int size = (int)m_textureSize.x;
        if (m_cb != null)
        {
            mainCamera.RemoveCommandBuffer(CameraEvent.AfterReflections, m_cb);
        }
        m_cb = new CommandBuffer();
        m_cb.name = "HiZ Commands";

        int width = mainCamera.pixelWidth;
        int height = mainCamera.pixelHeight;

        RenderTargetIdentifier hizID = new RenderTargetIdentifier(m_hizDepthBuffer);
        m_cb.Blit(null, hizID, m_generateBufferMat, 0);//blit
        int tempRT, lastRT = -1;
        for (int i = 0; i < m_HiZCount - 1; i++)
        {
            tempRT = Shader.PropertyToID("huanghaoran_temp" + i.ToString());
            width >>= 1;
            height >>= 1;
            m_cb.GetTemporaryRT(tempRT, width, height, 0, FilterMode.Point, RenderTextureFormat.RGHalf, RenderTextureReadWrite.Linear);
            if (i == 0)
            {
                m_cb.Blit(hizID, tempRT, m_generateBufferMat, 1);//Reduce
            }
            else
            {
                m_cb.Blit(lastRT, tempRT, m_generateBufferMat, 1);//Reduce
            }
            m_cb.CopyTexture(tempRT, 0, 0, hizID, 0, i + 1);

            if (lastRT != -1)
            {
                m_cb.ReleaseTemporaryRT(lastRT);
            }
            lastRT = tempRT;
        }
        m_cb.ReleaseTemporaryRT(lastRT);
        mainCamera.AddCommandBuffer(CameraEvent.AfterReflections, m_cb);
    }

    public void InitializeHiZBuffer()
    {
        if (m_hizDepthBuffer != null)
        {
            m_hizDepthBuffer.Release();
        }

        int width = mainCamera.pixelWidth;
        int height = mainCamera.pixelHeight;

        m_textureSize.x = width;
        m_textureSize.y = height;

        m_HiZCount = Mathf.Min(Mathf.FloorToInt(Mathf.Log(Mathf.Max(width, height), 2)), 11);

        m_hizDepthBuffer = new RenderTexture(mainCamera.pixelWidth, mainCamera.pixelHeight, 0, RenderTextureFormat.RGHalf, RenderTextureReadWrite.Linear);
        m_hizDepthBuffer.filterMode = FilterMode.Point;
        m_hizDepthBuffer.useMipMap = true;
        m_hizDepthBuffer.autoGenerateMips = false;
        m_hizDepthBuffer.Create();
        m_hizDepthBuffer.hideFlags = HideFlags.HideAndDontSave;
    }

    private void OnDisable()
    {
        if (mainCamera != null)
        {
            if (m_cb != null)
            {
                mainCamera.RemoveCommandBuffer(CameraEvent.AfterReflections, m_cb);
                m_cb = null;
            }
        }
        if (m_hizDepthBuffer != null)
        {
            m_hizDepthBuffer.Release();
            m_hizDepthBuffer = null;
        }
    }
}
